package module

import (
	"errors"
	"my_im2/server/_interface"
	"sync"
)

type connInfo struct {
	logined bool   //已登录
	user    string //登录后对应的用户
}

type userInfo struct {
	name    string
	pass    string
	friends map[string]struct{}
	groups  map[string]struct{}
}

type groupInfo struct {
	name    string
	members map[string]struct{}
}

type DbModule struct {
	server _interface.Server

	connInfoMap     map[uint]*connInfo
	connInfoMapLock sync.RWMutex

	userInfoMap     map[string]*userInfo
	userInfoMapLock sync.RWMutex

	groupInfoMap     map[string]*groupInfo
	groupInfoMapLock sync.RWMutex
}

func NewDbModule(server _interface.Server) *DbModule {
	return &DbModule{
		server:       server,
		connInfoMap:  make(map[uint]*connInfo),
		userInfoMap:  make(map[string]*userInfo),
		groupInfoMap: make(map[string]*groupInfo),
	}
}

func (d *DbModule) IsConnLogined(id uint) bool {
	d.connInfoMapLock.RLock()
	defer d.connInfoMapLock.RUnlock()

	if info, ok := d.connInfoMap[id]; ok {
		return info.logined
	}
	return false
}

func (d *DbModule) AddConn(id uint) {
	d.connInfoMapLock.Lock()
	defer d.connInfoMapLock.Unlock()

	d.connInfoMap[id] = &connInfo{
		logined: false,
	}
}

func (d *DbModule) GetConnId(user string) (uint, bool) {
	d.connInfoMapLock.RLock()
	defer d.connInfoMapLock.RUnlock()

	for id, info := range d.connInfoMap {
		if info.user == user {
			return id, true
		}
	}
	return 0, false
}

func (d *DbModule) updateConnUser(id uint, user string) {
	d.connInfoMapLock.Lock()
	defer d.connInfoMapLock.Unlock()

	if info, ok := d.connInfoMap[id]; ok {
		info.logined = true
		info.user = user
	}
}

func (d *DbModule) GetConnUser(id uint) (string, bool) {
	d.connInfoMapLock.RLock()
	defer d.connInfoMapLock.RUnlock()

	if info, ok := d.connInfoMap[id]; ok {
		return info.user, true
	}
	return "", false
}

func (d *DbModule) ConnDisconnect(id uint) {
	d.connInfoMapLock.Lock()
	defer d.connInfoMapLock.Unlock()

	delete(d.connInfoMap, id)
}

func (d *DbModule) ConnLogout(user string) {
	if id, ok := d.GetConnId(user); ok {
		d.connInfoMapLock.RLock()
		defer d.connInfoMapLock.RUnlock()

		info := d.connInfoMap[id]
		info.logined = false
		info.user = ""
	}
}

func (d *DbModule) IsUserExist(user string) bool {
	d.userInfoMapLock.RLock()
	defer d.userInfoMapLock.RUnlock()

	_, ok := d.userInfoMap[user]
	return ok
}

func (d *DbModule) IsUserLogined(user string) bool {
	_, ok := d.GetConnId(user)
	return ok
}

func (d *DbModule) AddUser(user string, passwd string) {
	u := &userInfo{
		name:    user,
		pass:    passwd,
		friends: make(map[string]struct{}),
		groups:  make(map[string]struct{}),
	}

	d.userInfoMapLock.Lock()
	defer d.userInfoMapLock.Unlock()

	d.userInfoMap[user] = u
}

func (d *DbModule) Login(id uint, user string, passwd string) error {
	d.userInfoMapLock.RLock()
	defer d.userInfoMapLock.RUnlock()

	if u, ok := d.userInfoMap[user]; ok {
		if _, ok := d.GetConnId(user); ok {
			return errors.New("用户已登录")
		}
		if u.pass != passwd {
			return errors.New("密码错误")
		}
		//用户名密码正确，关联当前conn与用户
		d.updateConnUser(id, user)
		return nil
	}
	return errors.New("用户不存在")
}

func (d *DbModule) GetUsersWhoHasFriend(user string) []string {
	d.userInfoMapLock.RLock()
	defer d.userInfoMapLock.RUnlock()

	ret := make([]string, 0)
	for u, info := range d.userInfoMap {
		if _, ok := info.friends[user]; ok {
			ret = append(ret, u)
		}
	}
	return ret
}

func (d *DbModule) IsUserHasFriend(user string, friend string) bool {
	d.userInfoMapLock.RLock()
	defer d.userInfoMapLock.RUnlock()

	if u, ok := d.userInfoMap[user]; ok {
		if _, ok := u.friends[friend]; ok {
			return true
		}
	}
	return false
}

func (d *DbModule) AddUserFriend(user string, friend string) {
	d.userInfoMapLock.RLock()
	defer d.userInfoMapLock.RUnlock()

	if u, ok := d.userInfoMap[user]; ok {
		u.friends[friend] = struct{}{}
	}
}

func (d *DbModule) DelUserFriend(user string, friend string) {
	d.userInfoMapLock.RLock()
	defer d.userInfoMapLock.RUnlock()

	if u, ok := d.userInfoMap[user]; ok {
		delete(u.friends, friend)
	}
}

func (d *DbModule) GetMyFriendsOnlineStatus(user string) ([]string, []string) {
	d.userInfoMapLock.RLock()
	defer d.userInfoMapLock.RUnlock()

	onlineFriends := make([]string, 0)
	offlineFriends := make([]string, 0)
	for f := range d.userInfoMap[user].friends {
		if _, ok := d.GetConnId(f); ok {
			onlineFriends = append(onlineFriends, f)
		} else {
			offlineFriends = append(offlineFriends, f)
		}
	}
	return onlineFriends, offlineFriends
}

func (d *DbModule) UserExitAllGroup(user string) {
	d.userInfoMapLock.RLock()
	defer d.userInfoMapLock.RUnlock()

	d.groupInfoMapLock.Lock()
	defer d.groupInfoMapLock.Unlock()

	for group := range d.userInfoMap[user].groups {
		g := d.groupInfoMap[group]
		delete(g.members, user)
		if len(g.members) == 0 {
			delete(d.groupInfoMap, group)
		}
	}
}

func (d *DbModule) DelMeFromAllUsers(user string) {
	d.userInfoMapLock.Lock()
	defer d.userInfoMapLock.Unlock()

	for _, u := range d.userInfoMap {
		delete(u.friends, user)
	}
}

func (d *DbModule) DelUser(user string) {
	d.userInfoMapLock.Lock()
	defer d.userInfoMapLock.Unlock()

	delete(d.userInfoMap, user)
}

func (d *DbModule) IsGroupExist(group string) bool {
	d.groupInfoMapLock.RLock()
	defer d.groupInfoMapLock.RUnlock()

	_, ok := d.groupInfoMap[group]
	return ok
}

func (d *DbModule) AddGroup(group string) {
	g := &groupInfo{
		name:    group,
		members: make(map[string]struct{}),
	}

	d.groupInfoMapLock.Lock()
	defer d.groupInfoMapLock.Unlock()

	d.groupInfoMap[group] = g
}

func (d *DbModule) AddGroupMember(group string, user string) {
	d.groupInfoMapLock.Lock()
	defer d.groupInfoMapLock.Unlock()

	if g, ok := d.groupInfoMap[group]; ok {
		g.members[user] = struct{}{}
	}

	d.userInfoMapLock.Lock()
	defer d.userInfoMapLock.Unlock()

	if u, ok := d.userInfoMap[user]; ok {
		u.groups[group] = struct{}{}
	}
}

func (d *DbModule) DelGroupMember(group string, user string) {
	d.groupInfoMapLock.Lock()
	defer d.groupInfoMapLock.Unlock()

	if g, ok := d.groupInfoMap[group]; ok {
		delete(g.members, user)
	}

	d.userInfoMapLock.Lock()
	defer d.userInfoMapLock.Unlock()

	if u, ok := d.userInfoMap[user]; ok {
		delete(u.groups, group)
	}
}

func (d *DbModule) GetGroupMembers(group string) []string {
	d.groupInfoMapLock.RLock()
	defer d.groupInfoMapLock.RUnlock()

	ret := make([]string, 0)
	if g, ok := d.groupInfoMap[group]; ok {
		for m := range g.members {
			ret = append(ret, m)
		}
	}
	return ret
}

func (d *DbModule) IsGroupHasMember(group string, user string) bool {
	d.groupInfoMapLock.RLock()
	defer d.groupInfoMapLock.RUnlock()

	if g, ok := d.groupInfoMap[group]; ok {
		if _, ok := g.members[user]; ok {
			return true
		}
	}
	return false
}

func (d *DbModule) TryCleanGroup(group string) bool {
	d.groupInfoMapLock.Lock()
	defer d.groupInfoMapLock.Unlock()

	if g, ok := d.groupInfoMap[group]; ok {
		if len(g.members) == 0 {
			delete(d.groupInfoMap, group)
			return true
		}
	}
	return false
}
